package com.aivin.adovertool.util;


import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


/**
 * 子线程工具类 ，支持： <br>
 * 1、子线程单次执行任务  <br>
 * 2、延时执行任务  <br>
 * 3、循环执行任务  <br>
 * @author ly-dengpw
 */
public class LyHeartBeatTool {
    private ScheduledExecutorService mScheduledExecutorService =null;
    private Map<String, Future<?>> futureHashMap;

    public static LyHeartBeatTool getInstance() {
        return SingletonFactory.INSTANCE;
    }

    private static class SingletonFactory {
        private static final LyHeartBeatTool INSTANCE = new LyHeartBeatTool();
    }

    private LyHeartBeatTool() {
        initTool();
    }

    private void initTool() {
        ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("ly-pool-%d").build();
        // 线程池拒绝策略: 丢弃任务并抛出 RejectedExecutionException 异常。
        RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy();
        int corePoolSize =5 ;
        // 创建一个支持定时及周期性的任务执行的线程池 (线程池大小  5)
        mScheduledExecutorService = new ScheduledThreadPoolExecutor(corePoolSize,threadFactory ,rejectedExecutionHandler) ;
        futureHashMap = new HashMap<>(16);
    }

    /**
     * 开始 循环执行任务
     * @param  period 单位为毫秒
     */
    public void startTheFixedRateRunnable(BaseLyRunable runable, int period) {
        if(mScheduledExecutorService==null){
            return;
        }

        if (futureHashMap == null) {
            return;
        }

        if (futureHashMap.containsKey(runable.getRunnableName())) {
            return;
        }
        Future<?> future = mScheduledExecutorService.scheduleAtFixedRate(runable, 0, period, TimeUnit.MILLISECONDS);
        futureHashMap.put(runable.getRunnableName(), future);
    }

    public void startTheFixedRateRunnableWithDelay(BaseLyRunable runable, int period ,int deley) {
        if(mScheduledExecutorService==null){
            return;
        }

        if (futureHashMap == null) {
            return;
        }

        if (futureHashMap.containsKey(runable.getRunnableName())) {
            return;
        }
        Future<?> future = mScheduledExecutorService.scheduleAtFixedRate(runable, deley, period, TimeUnit.MILLISECONDS);
        futureHashMap.put(runable.getRunnableName(), future);
    }

    /**
     * 延时执行一个 Runnable
     * @param  delayTime 单位为毫秒
     */
    public void startOneDelayedRunnable(BaseLyRunable runable, int delayTime) {
        if(mScheduledExecutorService==null){
            return;
        }

        if (futureHashMap == null) {
            return;
        }

        Future<?> future = mScheduledExecutorService.schedule(runable, delayTime, TimeUnit.MILLISECONDS);
        futureHashMap.put(runable.getRunnableName(), future);
    }

    /**
     * 关闭指定的 Runnable
     */
    public void shutDownTheRunable(BaseLyRunable runable) {
        if (futureHashMap == null) {
            return;
        }
        Future<?> future = futureHashMap.get(runable.getRunnableName());

        if (future != null) {
            //中断线程停止任务
            future.cancel(true);
            futureHashMap.remove(runable.getRunnableName());
        }
    }


    /**
     * 等应用程序退出或决定销毁单例时，调用这个方法。
     * 销毁所有任务
     */
    public void shutDownAllRunnable() {
        if (mScheduledExecutorService != null) {
            mScheduledExecutorService.shutdownNow();// 强制关闭
            mScheduledExecutorService = null;
        }

        if (futureHashMap!=null){
            futureHashMap.clear();
            futureHashMap = null;
        }
    }


    public static abstract class BaseLyRunable implements Runnable {
        private final String runnableName;
        public BaseLyRunable( ){
            runnableName = "runnable_"+System.nanoTime() ;
        }

        public String getRunnableName() {
            return runnableName;
        }
    }
}
